/**
 * Copyright © 2014 Creativor Studio.
 * About license information, please see LICENSE.txt.
 */
package org.rayson.tool.apt;

import java.util.HashMap;
import java.util.List;

import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.QualifiedNameable;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.AbstractElementVisitor7;
import javax.tools.Diagnostic.Kind;

/**
 * TODO Add type description here.
 *
 * @author creativor
 */
final class ProtocolMetaData {

	private class ProtocolVistor extends
			AbstractElementVisitor7<Void, JavaCode> {

		@Override
		public Void visitExecutable(final ExecutableElement e, final JavaCode p) {
			if (e.getKind() != ElementKind.METHOD)
				return null;

			processingEnv.getMessager().printMessage(Kind.NOTE,
					"visitExecutable method:" + e);

			// Generate method meta-data.
			final List<? extends VariableElement> params = e.getParameters();

			final StringBuffer sb = new StringBuffer();
			sb.append(METHODS_FIELD_NAME + ".put(\"" + e.getSimpleName()
					+ "\", \"[");
			for (final VariableElement parameter : params) {
				sb.append("\\\"");
				sb.append(parameter.getSimpleName());
				sb.append("\\\"");
				sb.append(",");
			}
			if (params.size() > 0)
				sb.deleteCharAt(sb.length() - 1);
			sb.append("]\")");
			p.appendCodeLine(sb.toString());
			return null;
		}

		@Override
		public Void visitPackage(final PackageElement e, final JavaCode p) {
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public Void visitType(final TypeElement e, final JavaCode p) {
			processingEnv.getMessager().printMessage(Kind.NOTE,
					"visitType:" + e);
			return null;
		}

		@Override
		public Void visitTypeParameter(final TypeParameterElement e,
				final JavaCode p) {
			processingEnv.getMessager().printMessage(Kind.NOTE,
					"visitTypeParameter:" + e);
			return null;
		}

		@Override
		public Void visitVariable(final VariableElement e, final JavaCode p) {
			processingEnv.getMessager().printMessage(Kind.NOTE,
					"visitVariable:" + e);
			return null;
		}

	}

	private final static String META_DATA_CLASS_SUFFIX = "$rayson$proto";

	private static final String VSERSION_FIELD_NAME = "VERSION";

	private static final short DEFAULT_VERSION = 1;

	private static final String NAME_FIELD_NAME = "NAME";

	private static final String METHODS_FIELD_NAME = "METHODS";

	private final TypeElement protocolType;

	private final String classSimpleName;
	private final String packageName;

	private final ProcessingEnvironment processingEnv;

	/**
	 * @param protocolType
	 */
	ProtocolMetaData(final TypeElement protocolType,
			final ProcessingEnvironment processingEnv) {
		this.protocolType = protocolType;
		this.processingEnv = processingEnv;
		this.classSimpleName = protocolType.getSimpleName()
				+ META_DATA_CLASS_SUFFIX;
		this.packageName = ((QualifiedNameable) this.protocolType
				.getEnclosingElement()).getQualifiedName().toString();
	}

	/**
	 * @return
	 */
	public CharSequence generateJavaCode() {
		final JavaCode code = new JavaCode();
		code.appendCodeLine("package " + packageName);
		code.appendImport(HashMap.class);
		// Begin class.
		code.appendLine("public class " + classSimpleName);
		code.beginBracket();
		// Constructor.
		code.appendLine("private " + classSimpleName + "()");
		code.beginBracket();
		code.endBracket();
		// Meta Data.
		code.addPublicConstant(VSERSION_FIELD_NAME, Short.TYPE, DEFAULT_VERSION);
		code.addPublicConstant(NAME_FIELD_NAME, String.class, "\""
				+ protocolType.getQualifiedName() + "\"");
		// Add methods info.
		code.appendCodeLine("public static final HashMap<String, String> "
				+ METHODS_FIELD_NAME + " = new HashMap<String, String>()");
		code.appendLine("static");
		code.beginBracket();

		final ProtocolVistor v = new ProtocolVistor();

		for (final Element element : protocolType.getEnclosedElements()) {
			element.accept(v, code);
		}

		code.endBracket();
		// End class.
		code.endBracket();
		return code.toString();
	}

	public CharSequence getClassSimpleName() {
		return this.classSimpleName;
	}

	public String getPackageName() {
		return this.packageName;
	}
}